#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/threadmgr.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/audioout.h>
#include <psp2/avplayer.h>

#include <vita2d.h>

#include "avplayer.h"
#include "avplaylist.h"
#include "utils.h"
#include "ui.h"
#include "config.h"
#include "file.h"
#include "lang.h"
#include "listview.h"

#ifndef ALIGN
#define ALIGN(x, a) (((x) + ((a)-1)) & ~((a)-1))
#endif

#define DEFAULT_AUDIO_VOL 32767
#define FRAMEBUFFER_ALIGNMENT 0x40000

#define BIG_JUMP_STEP_TIME (15 * 1000)
#define SMALL_JUMP_STEP_TIME (1 * 1000)

#define SECOND_CONVERT_MICROS 1000000llu

#define FPS_DELAY_MICROS (SECOND_CONVERT_MICROS / 60)
#define SAVE_DELAY_MICROS (SECOND_CONVERT_MICROS * 1)
#define JUMP_DELAY_MICROS (SECOND_CONVERT_MICROS / 10)
#define CHANGE_CONTROLS_VIEW_DELTA_MICROS 250000llu
#define CONTROLS_VIEW_SHOW_MICROS (SECOND_CONVERT_MICROS * 5)
#define DOUBLE_CLICK_DELTA_MICROS 240000llu

#define MAX_TRICK_SPEED_MODE_NUM 32
#define TRICK_SPEED_NORMAL 100
#define LONG_CLICK_TRICK_SPEED_MODE 8

#define AVPLAYER_PADDING_T 10.0f
#define AVPLAYER_PADDING_B 0.0f
#define AVPLAYER_PADDING_L 10.0f

#define BOTTOM_MESSAGE_MARIN_L 0.f

#define TEXT_BUTTON_PADDING 10.f
#define TEXT_BUTTON_HEIGHT (UI_LINE_HEIGHT + TEXT_BUTTON_PADDING * 2)

#define PROGRESS_BAR_BG_COLOR COLOR_ALPHA(LITEGRAY, 0xAF)
#define PROGRESS_BAR_COLOR SPRING_GREEN

#define PROGRESS_BAR_VIEW_W (SCREEN_WIDTH - AVPLAYER_PADDING_L * 2)
#define PROGRESS_BAR_VIEW_H 28.0f
#define PROGRESS_BAR_VIEW_PADDING_T 10.f

#define PROGRESS_BAR_TOTAL_W PROGRESS_BAR_VIEW_W
#define PROGRESS_BAR_TOTAL_H (PROGRESS_BAR_VIEW_H - PROGRESS_BAR_VIEW_PADDING_T * 2)
#define PROGRESS_BAR_MARGIN_T 4.0f
#define PROGRESS_BAR_MARGIN_B 4.0f

#define PROGRESS_BAR_FOCUS_VIEW_W 16.0f
#define PROGRESS_BAR_FOCUS_VIEW_H PROGRESS_BAR_VIEW_H

#define LISTVIEW_PADDING_L 10.0f
#define LISTVIEW_PADDING_R 10.0f
#define LISTVIEW_PADDING_T 10.0f
#define LISTVIEW_PADDING_B 10.0f
#define LISTVIEW_W 400.0f
#define LISTVIEW_H SCREEN_HEIGHT

enum ChangeControlsViewType
{
    CHANGE_CONTROLS_VIEW_NONE,
    CHANGE_CONTROLS_VIEW_SHOW,
    CHANGE_CONTROLS_VIEW_CLOSE,
};

typedef struct
{
    char *text;
    int looping;
    int enable;
} PlayModeOption;

static ListView *avplayListView = NULL;

static SceAvPlayerFrameInfo video_frame;
static SceAvPlayerHandle avplayer_handle;

static vita2d_texture *current_tex = NULL;
static vita2d_texture *old_tex = NULL;

static int avplayer_ready = 0;
static int avplayer_playing = 0;
static int avplayer_close = 0;

static SceUID audio_thid = -1;
static int audio_port = -1;
static int audio_stop = 0;
static int audio_vol = DEFAULT_AUDIO_VOL;

static SceUID save_thid = -1;
static int save_stop = 0;

static int trick_spead_mode = 1;

static uint64_t av_current_time = 0, av_total_time = 0, av_jump_time = 0, av_ini_jump_time = 0, av_focus_time = 0;
static uint64_t save_last_micros = 0, save_delay_micros = 0;
static uint64_t jump_last_micros = 0, jump_delay_micros = 0;
static uint64_t change_controls_view_begin_micros = 0;
static uint64_t show_controls_view_final_micros = 0;
static uint64_t last_released_touch_micros = 0;

static int show_controls_view = 0;
static int show_list_view = 0;
static int total_time_inited = 0;

static int change_controls_view_type = CHANGE_CONTROLS_VIEW_NONE;

static int avplayer_suspend_locked = 0;
static int avplayer_paly_mode = PLAY_MODE_SINGLE;

static AvplayerConfig avplayer_config;
static AvplayList *avplaypList = NULL;

static char avplayer_config_path[MAX_PATH_LENGTH];
static char avplayer_save_path[MAX_PATH_LENGTH];
static char avplayer_file_name[MAX_NAME_LENGTH];

static int controls_view_sx = 0, controls_view_sy = 0, controls_view_dx = SCREEN_WIDTH, controls_view_dy = SCREEN_HEIGHT;
static int progress_bar_view_sx = 0, progress_bar_view_sy = 0, progress_bar_view_dx = 0, progress_bar_view_dy = 0;
static int play_button_sx = 0, play_button_sy = 0, play_button_dx = 0, play_button_dy = 0;
static int paly_mode_sx = 0, paly_mode_sy = 0, paly_mode_dx = 0, paly_mode_dy = 0;
static int display_mode_sx = 0, display_mode_sy = 0, display_mode_dx = 0, display_mode_dy = 0;
static int play_list_sx = 0, play_list_sy = 0, play_list_dx = 0, play_list_dy = 0;
static int listview_sx = 0, listview_sy = 0, listview_dx = 0, listview_dy = 0, listview_current_sx = 0;

static int playlist_current_pos = 0;

static PlayModeOption play_mode_options[] = {
    {STR_PLAY_MODE_SINGLE, 0, 1},
    {STR_PLAY_MODE_SEQUENTIAL, 0, 1},
    {STR_PLAY_MODE_LOOP_SINGLE, 1, 1},
    {STR_PLAY_MODE_LOOP_ALL, 0, 1},
};
#define N_PLAY_MODE_OPTIOMS (sizeof(play_mode_options) / sizeof(PlayModeOption))

static int AvplayerOpenBase(char *path, int play_mode);
static void AvplayerDraw(vita2d_texture *texture);
static void avplayerOpenListView();
static void avplayerCloseListView();
static void avplayerCloseListViewPrompt();

static void *memalloc(void *p, uint32_t alignment, uint32_t size)
{
    return memalign(alignment, size);
}

static void dealloc(void *p, void *ptr)
{
    free(ptr);
}

static void *gpu_alloc(void *p, uint32_t alignment, uint32_t size)
{
    void *res = NULL;

    if (alignment < FRAMEBUFFER_ALIGNMENT)
        alignment = FRAMEBUFFER_ALIGNMENT;

    size = ALIGN(size, alignment);
    SceKernelAllocMemBlockOpt opt;
    memset(&opt, 0, sizeof(opt));
    opt.size = sizeof(SceKernelAllocMemBlockOpt);
    opt.attr = SCE_KERNEL_ALLOC_MEMBLOCK_ATTR_HAS_ALIGNMENT;
    opt.alignment = alignment;
    SceUID memblock = sceKernelAllocMemBlock("Video Memblock", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, size, &opt);
    sceKernelGetMemBlockBase(memblock, &res);
    sceGxmMapMemory(res, size, (SceGxmMemoryAttribFlags)(SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE));
    return res;
}

static void gpu_dealloc(void *p, void *ptr)
{
    SceUID memblock = sceKernelFindMemBlockByAddr(ptr, 0);
    sceGxmUnmapMemory(ptr);
    sceKernelFreeMemBlock(memblock);
}

static void changeControlsView(int type)
{
    if (change_controls_view_type != type)
        change_controls_view_begin_micros = sceKernelGetProcessTimeWide();
    change_controls_view_type = type;
}

static void showControlsView()
{
    changeControlsView(CHANGE_CONTROLS_VIEW_SHOW);
}

static void closeControlsView()
{
    changeControlsView(CHANGE_CONTROLS_VIEW_CLOSE);
}

static void refreshListViewX()
{
    if (show_list_view)
    {
        if (listview_current_sx > listview_sx)
        {
            listview_current_sx -= LISTVIEW_W / 10;
            if (listview_current_sx < listview_sx)
                listview_current_sx = listview_sx;
            ListViewSetLayoutXY(avplayListView, listview_current_sx, listview_sy);
        }
    }
    else
    {
        if (listview_current_sx < listview_dx)
        {
            listview_current_sx += LISTVIEW_W / 10;
            if (listview_current_sx > listview_dx)
                listview_current_sx = listview_dx;
            ListViewSetLayoutXY(avplayListView, listview_current_sx, listview_sy);
        }
    }
}

static void avplayerLockSuspend()
{
    if (!avplayer_suspend_locked)
    {
        lockSuspend();
        lockOledOff();
        lockOledDimming();
        avplayer_suspend_locked = 1;
    }
}

static void avplayerUnlockSuspend()
{
    if (avplayer_suspend_locked)
    {
        unlockSuspend();
        unlockOledOff();
        unlockOledDimming();
        avplayer_suspend_locked = 0;
    }
}

void makeAvplayerConfigPath(char *path)
{
    char base_name[MAX_NAME_LENGTH];
    makeBaseName(base_name, path, MAX_NAME_LENGTH);
    snprintf(avplayer_config_path, MAX_PATH_LENGTH, "%s/%s.cfg", (APP_SAVEFILES_DIR), base_name);
}

void makeAvplayerSavePath(char *path)
{
    char base_name[MAX_NAME_LENGTH];
    makeBaseName(base_name, path, MAX_NAME_LENGTH);
    snprintf(avplayer_save_path, MAX_PATH_LENGTH, "%s/%s.sav", (APP_SAVEFILES_DIR), base_name);
}

int setDefaultAvplayerConfig()
{
    memset(&avplayer_config, 0, sizeof(avplayer_config));
    avplayer_config.version = AVPLAYER_CONFIG_VERSION;
    avplayer_config.display_mode = DISPLAY_PRESERVE_FILL;

    return 0;
}

static int loadAvplayerConfig()
{
    AvplayerConfig config;
    memset(&config, 0, sizeof(AvplayerConfig));

    int ret = ReadFile(avplayer_config_path, &config, sizeof(AvplayerConfig));
    if (ret < 0 || ret != sizeof(AvplayerConfig) || config.version != AVPLAYER_CONFIG_VERSION)
    {
        setDefaultAvplayerConfig();
        return -1;
    }

    memcpy(&avplayer_config, &config, sizeof(AvplayerConfig));

    return 0;
}

static int saveAvplayerConfig()
{
    return WriteFile(avplayer_config_path, &avplayer_config, sizeof(AvplayerConfig));
}

static int AvplayerSetPlaylist(FileList *list)
{
    if (avplaypList)
        avplayListEmpty(avplaypList);
    else
        avplaypList = (AvplayList *)calloc(1, sizeof(AvplayList));

    return avplayListGetEntries(avplaypList, list);
}

static void AvplayerCleanPlaylist()
{
    if (avplaypList)
        avplayListEmpty(avplaypList);
    free(avplaypList);
    avplaypList = NULL;
}

static int AvplayerOpenFromListByNumber(int n, int play_mode, int replay)
{
    if (!avplaypList)
        return -1;

    if (replay)
    {
        AvplayerTerm();
        AvplayerInit();
    }

    AvplayListEntry *entry = avplayListGetEntryByNumber(avplaypList, n);
    if (!entry)
        return -1;

    playlist_current_pos = n;

    return AvplayerOpenBase(entry->path, play_mode);
}

static int AvplayerOpenFromListByName(char *name, int play_mode, int replay)
{
    if (!avplaypList)
        return -1;

    int n = avplayListGetNumberByName(avplaypList, name);
    if (n < 0)
        return -1;

    return AvplayerOpenFromListByNumber(n, play_mode, replay);
}

static int audioThreadCallback(unsigned int args, void *arg)
{
    SceAvPlayerFrameInfo audio_frame;
    memset(&audio_frame, 0, sizeof(SceAvPlayerFrameInfo));

    sceAudioOutSetConfig(audio_port, -1, -1, (SceAudioOutMode)-1);

    audio_vol = DEFAULT_AUDIO_VOL;
    int vol_stereo[] = {audio_vol, audio_vol};
    sceAudioOutSetVolume(audio_port, (SceAudioOutChannelFlag)(SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH), vol_stereo);

    while (!audio_stop)
    {
        if (!avplayer_ready || !sceAvPlayerIsActive(avplayer_handle) || !sceAvPlayerGetAudioData(avplayer_handle, &audio_frame))
        {
            sceKernelDelayThread(1000);
            continue;
        }

        if (vol_stereo[0] != audio_vol || vol_stereo[1] != audio_vol)
        {
            vol_stereo[0] = vol_stereo[1] = audio_vol;
            sceAudioOutSetVolume(audio_port, (SceAudioOutChannelFlag)(SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH), vol_stereo);
        }

        sceAudioOutSetConfig(audio_port, -1, audio_frame.details.audio.sampleRate, audio_frame.details.audio.channelCount == 1 ? SCE_AUDIO_OUT_MODE_MONO : SCE_AUDIO_OUT_MODE_STEREO);
        sceAudioOutOutput(audio_port, audio_frame.pData);
    }

    return sceKernelExitDeleteThread(0);
}

static void audioShutdown()
{
    audio_stop = 1;

    if (audio_thid != -1)
    {
        sceKernelWaitThreadEnd(audio_thid, NULL, NULL);
        sceKernelDeleteThread(audio_thid);
        audio_thid = -1;
    }
    if (audio_port != -1)
    {
        sceAudioOutReleasePort(audio_port);
        audio_port = -1;
    }
}

static int audioInit()
{
    if (audio_port != -1 || audio_thid != -1)
        audioShutdown();

    audio_stop = 0;

    audio_port = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_BGM, 1024, 48000, SCE_AUDIO_OUT_MODE_STEREO);
    if (audio_port < 0)
    {
        audio_port = -1;
        audioShutdown();
        return -1;
    }

    audio_thid = sceKernelCreateThread("avplayer_audio_thread", &audioThreadCallback, 0x10000100, 0x10000, 0, 0, NULL);
    if (audio_thid < 0)
    {
        audio_thid = -1;
        audioShutdown();
        return -1;
    }
    if (sceKernelStartThread(audio_thid, 0, NULL) != 0)
    {
        audioShutdown();
        return -1;
    }

    return 0;
}

static int loadAvplayerTime()
{
    uint64_t time = 0;

    int ret = ReadFile(avplayer_save_path, &time, sizeof(uint64_t));
    if (ret < 0 || ret != sizeof(uint64_t))
        return -1;

    av_ini_jump_time = time;

    return 0;
}

static int saveAvplayerTime()
{
    uint64_t current_time = av_current_time;
    uint64_t total_time = av_total_time;
    if (total_time > 0)
        return WriteFile(avplayer_save_path, &av_current_time, sizeof(uint64_t));

    return -1;
}

static int saveThreadCallback(unsigned int args, void *arg)
{
    uint64_t cur_micros = 0, delta_micros = 0;

    while (!save_stop)
    {
        saveAvplayerTime();

        cur_micros = sceKernelGetProcessTimeWide();
        delta_micros = cur_micros - save_last_micros;
        if (delta_micros < save_delay_micros)
        {
            sceKernelDelayThread(save_delay_micros - delta_micros);
            save_last_micros += save_delay_micros;
        }
        else
        {
            save_last_micros = cur_micros;
        }
    }

    return sceKernelExitDeleteThread(0);
}

static void saveThreadShutdown()
{
    save_stop = 1;
    if (save_thid != -1)
    {
        sceKernelWaitThreadEnd(save_thid, NULL, NULL);
        sceKernelDeleteThread(save_thid);
        save_thid = -1;
    }
}

static int saveThreadInit()
{
    if (save_thid != -1)
        saveThreadShutdown();

    save_stop = 0;

    save_thid = sceKernelCreateThread("avplayer_save_thread", &saveThreadCallback, 0x10000100, 0x10000, 0, 0, NULL);
    if (save_thid < 0)
    {
        save_thid = -1;
        return -1;
    }
    if (sceKernelStartThread(save_thid, 0, NULL) != 0)
    {
        saveThreadShutdown();
        return -1;
    }

    return 0;
}

int AvplayerInit()
{
    if (avplayer_ready)
        return 0;

    int res;

    SceAvPlayerInitData init_data;
    memset(&init_data, 0, sizeof(SceAvPlayerInitData));
    init_data.memoryReplacement.allocate = memalloc;
    init_data.memoryReplacement.deallocate = dealloc;
    init_data.memoryReplacement.allocateTexture = gpu_alloc;
    init_data.memoryReplacement.deallocateTexture = gpu_dealloc;
    init_data.basePriority = 0xA0;
    init_data.numOutputVideoFrameBuffers = 2;
    init_data.autoStart = 1;
    avplayer_handle = sceAvPlayerInit(&init_data);

    memset(&video_frame, 0, sizeof(SceAvPlayerFrameInfo));

    avplayer_ready = 1;

    return 0;
}

void AvplayerTerm()
{
    if (!avplayer_ready)
        return;

    avplayer_ready = 0;

    AvplayerClose();
    sceAvPlayerClose(avplayer_handle);
}

static int AvplayerOpenBase(char *path, int play_mode)
{
    if (!avplayer_ready)
        return -1;

    int res;

    res = sceAvPlayerAddSource(avplayer_handle, path);
    if (res < 0)
    {
        printf("sceAvPlayerAddSource failed: path=%s\n", path);
        return res;
    }
    WriteFile((LASTFILE_PATH), path, strlen(path) + 1);

    avplayer_close = 0;

    total_time_inited = 0;
    av_total_time = 0;
    av_current_time = 0;
    av_focus_time = 0;
    av_jump_time = 0;
    av_ini_jump_time = 0;

    vita2d_wait_rendering_done();
    if (!current_tex)
        current_tex = (vita2d_texture *)malloc(sizeof(vita2d_texture));
    if (current_tex)
        current_tex->palette_UID = 0;
    old_tex = NULL;

    int looping = 0;
    if (play_mode == PLAY_MODE_SINGLE)
        looping = 1;
    sceAvPlayerSetLooping(avplayer_handle, looping);

    trick_spead_mode = 1;
    int trick_spead = trick_spead_mode * TRICK_SPEED_NORMAL;
    sceAvPlayerSetTrickSpeed(avplayer_handle, trick_spead);

    audioInit();
    saveThreadInit();

    createFolder(APP_SAVEFILES_DIR);
    makeFilename(avplayer_file_name, path, MAX_NAME_LENGTH);
    makeAvplayerConfigPath(path);
    makeAvplayerSavePath(path);
    loadAvplayerTime();
    loadAvplayerConfig();

    avplayer_playing = 1;
    showControlsView();

    return 0;
}

int AvplayerOpen(char *path, int looping)
{
    if (looping)
        avplayer_paly_mode = PLAY_MODE_LOOP_SINGLE;
    else
        avplayer_paly_mode = PLAY_MODE_SINGLE;

    play_mode_options[1].enable = 0;
    play_mode_options[3].enable = 0;
    show_list_view = 0;

    return AvplayerOpenBase(path, avplayer_paly_mode);
}

void AvplayerClose()
{
    avplayer_close = 1;

    audioShutdown();
    saveThreadShutdown();
    saveAvplayerTime();
    AvplayerStop();

    vita2d_wait_rendering_done();
    if (current_tex)
        vita2d_free_texture(current_tex);
    current_tex = NULL;
    old_tex = NULL;
}

int AvplayerPlay(char *path, int looping)
{
    int ret;

    AvplayerInit();
    ret = AvplayerOpen(path, looping);
    if (ret < 0)
        goto END;
    AvplayerRun();

END:
    AvplayerTerm();

    return ret;
}

static int getListLenghCallback(void *list)
{
    AvplayList *pList = (AvplayList *)list;
    if (pList)
        return pList->length;

    return 0;
}

static void *getEntryByNumberCallback(void *list, int index)
{
    AvplayList *pList = (AvplayList *)list;
    if (pList)
        return avplayListGetEntryByNumber(pList, index);

    return NULL;
}

static void *getNextEntryCallback(void *list, void *entry, int index)
{
    AvplayListEntry *pEntry = (AvplayListEntry *)entry;
    if (pEntry)
        return pEntry->next;

    return NULL;
}

static int getNameViewDataCallback(TextView *view, void *list, void *entry, int index)
{
    AvplayListEntry *pEntry = (AvplayListEntry *)entry;
    if (pEntry)
        view->datas.text = pEntry->name;

    if (index == playlist_current_pos)
        view->datas.text_color = SPRING_GREEN;
    else
        view->datas.text_color = WHITE;

    return 0;
}

static void onItemClick(ListView *listView)
{
    avplayerCloseListViewPrompt();
    AvplayerDraw(old_tex);

    FileList *list = (FileList *)listView->list;
    int focus_pos = ListViewGetFocusPos(listView);
    AvplayerOpenFromListByNumber(focus_pos, avplayer_paly_mode, 1);
}

static void avplayerCreatListView()
{
    if (!avplayListView)
    {
        playlist_current_pos = 0;

        listview_sx = SCREEN_WIDTH - LISTVIEW_W;
        listview_sy = (SCREEN_HEIGHT - LISTVIEW_H) / 2;
        listview_dx = SCREEN_WIDTH;
        listview_dy = SCREEN_HEIGHT;
        listview_current_sx = SCREEN_WIDTH;

        ListViewParams params;
        memset(&params, 0, sizeof(ListViewParams));
        params.layout_sx = listview_sx;
        params.layout_sy = listview_sy;
        params.layout_width = LISTVIEW_W;
        params.layout_height = LISTVIEW_H;
        params.padding_left = LISTVIEW_PADDING_L;
        params.padding_right = LISTVIEW_PADDING_R;
        params.padding_top = LISTVIEW_PADDING_T;
        params.padding_bottom = LISTVIEW_PADDING_B;
        params.listview_bg_color = DEFALUT_BG_COLOR;
        params.itemview_bg_color = COLOR_ALPHA(0x111111, 0xAF);
        params.enable_info = 0;
        params.enable_image = 0;
        params.enable_checkBox = 0;
        params.disable_divider = 1;

        ListViewCallbacks callbacks;
        memset(&callbacks, 0, sizeof(ListViewCallbacks));
        callbacks.getListLengh = getListLenghCallback;
        callbacks.getEntryByNumber = getEntryByNumberCallback;
        callbacks.getNextEntry = getNextEntryCallback;
        callbacks.getNameViewData = getNameViewDataCallback;

        avplayListView = NewListView(&params, &callbacks, avplaypList);
    }

    ListViewSetOnItemClikListener(avplayListView, &onItemClick);
}

static void avplayerDestroyListView()
{
    DestroyListView(avplayListView);
    avplayListView = NULL;
}

static void avplayerOpenListView()
{
    showControlsView();
    show_list_view = 1;
}

static void avplayerCloseListView()
{
    showControlsView();
    show_list_view = 0;
}

static void avplayerCloseListViewPrompt()
{
    avplayerCloseListView();
    listview_current_sx = listview_dx;
}

int AvplayerPlayWithFileList(FileList *list, char *name, int play_mode)
{
    int ret;

    avplayer_paly_mode = play_mode;
    play_mode_options[1].enable = 1;
    play_mode_options[3].enable = 1;
    show_list_view = 0;

    AvplayerSetPlaylist(list);
    avplayerCreatListView();

    AvplayerInit();
    ret = AvplayerOpenFromListByName(name, play_mode, 0);
    if (ret < 0)
        goto END;

    ListViewSetGamepadMode(avplayListView, 1);
    int top_pos = playlist_current_pos - ListViewGetDrawLines(avplayListView) / 2;
    ListViewRefreshPosByFocusPos(avplayListView, top_pos, playlist_current_pos, SCROLL_NONE);
    AvplayerRun();

END:
    AvplayerTerm();
    avplayerDestroyListView();
    AvplayerCleanPlaylist();

    return ret;
}

int AvplayerIsPlaying()
{
    return sceAvPlayerIsActive(avplayer_handle) && avplayer_playing;
}

int AvplayerPause()
{
    sceAvPlayerPause(avplayer_handle);
    avplayer_playing = 0;

    return 0;
}

int AvplayerResume()
{
    sceAvPlayerResume(avplayer_handle);
    avplayer_playing = 1;

    return 0;
}

int AvplayerStop()
{
    sceAvPlayerStop(avplayer_handle);
    avplayer_playing = 0;

    return 0;
}

uint64_t avplayerGetCurrentTime()
{
    return sceAvPlayerCurrentTime(avplayer_handle);
}

int AvplayerJumpToTime(int64_t timing)
{
    return sceAvPlayerJumpToTime(avplayer_handle, timing);
}

int AvplayerGetTrickSpeed()
{
    return trick_spead_mode * TRICK_SPEED_NORMAL;
}

int AvplayerSetTrickSpeed(int spead)
{
    trick_spead_mode = spead / 100;
    if (trick_spead_mode == 0)
        trick_spead_mode = 1;
    else if (trick_spead_mode < -32)
        trick_spead_mode = -32;
    else if (trick_spead_mode > 32)
        trick_spead_mode = 32;

    int trick_speed = trick_spead_mode * TRICK_SPEED_NORMAL;
    return sceAvPlayerSetTrickSpeed(avplayer_handle, trick_speed);
}

int AvplayerGetVol()
{
    return audio_vol;
}

void AvplayerSetVol(int vol)
{
    audio_vol = (vol < 0) ? 0 : ((vol > 32767) ? 32767 : vol);
}

vita2d_texture *AvPlayerGetVideoTexture()
{
    if (!avplayer_ready || !sceAvPlayerIsActive(avplayer_handle))
        return NULL;

    int ret;
    ret = sceAvPlayerGetVideoData(avplayer_handle, &video_frame);
    if (!ret)
        return NULL;

    if (!total_time_inited)
    {
        SceAvPlayerStreamInfo video_stream;
        memset(&video_stream, 0, sizeof(SceAvPlayerStreamInfo));
        sceAvPlayerGetStreamInfo(avplayer_handle, SCE_AVPLAYER_VIDEO, &video_stream);
        av_total_time = video_stream.duration;
        total_time_inited = 1;
    }

    if (av_ini_jump_time > 0)
    {
        if (av_ini_jump_time < av_total_time - 1000)
            sceAvPlayerJumpToTime(avplayer_handle, av_ini_jump_time);
        av_ini_jump_time = 0;

        ret = sceAvPlayerGetVideoData(avplayer_handle, &video_frame);
        if (!ret)
            return NULL;
    }

    sceGxmTextureInitLinear(
        &current_tex->gxm_tex,
        video_frame.pData,
        SCE_GXM_TEXTURE_FORMAT_YVU420P2_CSC1,
        video_frame.details.video.width,
        video_frame.details.video.height, 0);

    old_tex = current_tex;

    return current_tex;
}

static void AvplayerDraw(vita2d_texture *texture)
{
    UiStartDrawing();

    if (texture)
    {
        float base_width = (float)vita2d_texture_get_width(texture);
        float base_height = (float)vita2d_texture_get_height(texture);
        float aspect_ratio = base_width / base_height;
        float width, height;
        if (avplayer_config.display_mode == DISPLAY_FULL_FILL)
        {
            width = SCREEN_WIDTH;
            height = SCREEN_HEIGHT;
        }
        else
        {
            width = SCREEN_WIDTH;
            height = width / aspect_ratio;
            if (height > SCREEN_HEIGHT)
            {
                height = SCREEN_HEIGHT;
                width = height * aspect_ratio;
            }
        }

        float scale_x = width / base_width;
        float scale_y = height / base_height;
        int x = (SCREEN_WIDTH - width) / 2;
        int y = (SCREEN_HEIGHT - height) / 2;
        vita2d_draw_texture_scale(texture, x, y, scale_x, scale_y);
    }

    if (show_controls_view)
    {
        drawTopStatusBar(avplayer_file_name, DEFALUT_BG_COLOR);

        int sx = controls_view_sx + AVPLAYER_PADDING_L;
        int dx = controls_view_dx - AVPLAYER_PADDING_L;
        int dy = controls_view_dy - AVPLAYER_PADDING_B;

        play_button_sx = sx - TEXT_BUTTON_PADDING;
        play_button_sy = dy - TEXT_BUTTON_HEIGHT;
        play_button_dy = dy;

        progress_bar_view_sx = sx;
        progress_bar_view_dx = progress_bar_view_sx + PROGRESS_BAR_VIEW_W;
        progress_bar_view_dy = play_button_sy - PROGRESS_BAR_MARGIN_B;
        progress_bar_view_sy = progress_bar_view_dy - PROGRESS_BAR_VIEW_H;

        controls_view_sy = progress_bar_view_sy - PROGRESS_BAR_MARGIN_T - AVPLAYER_PADDING_T;

        vita2d_draw_rectangle(controls_view_sx, controls_view_sy, controls_view_dx - controls_view_sx,
                              controls_view_dy - controls_view_sy, DEFALUT_BG_COLOR);

        int play_button_text_sy = play_button_sy + TEXT_BUTTON_PADDING;
        char *play_button_text;
        if (avplayer_playing)
            play_button_text = STR_PLAYING;
        else
            play_button_text = STR_PAUSEING;
        play_button_dx = play_button_sx + UiGetTextWidth(play_button_text) + TEXT_BUTTON_PADDING * 2;
        UiDrawText(play_button_sx + TEXT_BUTTON_PADDING, play_button_text_sy, WHITE, play_button_text);

        uint64_t current_time = av_focus_time;
        uint64_t total_time = av_total_time;

        char current_time_str[16], total_time_str[16];
        getDurationString(current_time_str, current_time);
        getDurationString(total_time_str, total_time);
        char time_text[32];
        snprintf(time_text, 32, "%s/%s", current_time_str, total_time_str);
        int time_dx = dx + TEXT_BUTTON_PADDING;
        int time_sx = time_dx - UiGetTextWidth(time_text) - TEXT_BUTTON_PADDING * 2;
        UiDrawText(time_sx + TEXT_BUTTON_PADDING, play_button_text_sy, WHITE, time_text);

        char *paly_mode_text = play_mode_options[avplayer_paly_mode].text;
        paly_mode_dx = time_sx - BOTTOM_MESSAGE_MARIN_L;
        paly_mode_sx = paly_mode_dx - UiGetTextWidth(paly_mode_text) - TEXT_BUTTON_PADDING * 2;
        paly_mode_sy = play_button_sy;
        paly_mode_dy = play_button_dy;
        UiDrawText(paly_mode_sx + TEXT_BUTTON_PADDING, play_button_text_sy, WHITE, paly_mode_text);

        char *display_mode_text;
        if (avplayer_config.display_mode == DISPLAY_PRESERVE_FILL)
            display_mode_text = STR_PRESERVE_FILL;
        else
            display_mode_text = STR_FULL_FILL;
        display_mode_dx = paly_mode_sx - BOTTOM_MESSAGE_MARIN_L;
        display_mode_sx = display_mode_dx - UiGetTextWidth(display_mode_text) - TEXT_BUTTON_PADDING * 2;
        display_mode_sy = play_button_sy;
        display_mode_dy = play_button_dy;
        UiDrawText(display_mode_sx + TEXT_BUTTON_PADDING, play_button_text_sy, WHITE, display_mode_text);

        play_list_sx = display_mode_sx;
        if (avplayListView)
        {
            play_list_dx = display_mode_sx - BOTTOM_MESSAGE_MARIN_L;
            play_list_sx = play_list_dx - UiGetTextWidth(STR_PLAY_LIST) - TEXT_BUTTON_PADDING * 2;
            play_list_sy = play_button_sy;
            play_list_dy = play_button_dy;
            UiDrawText(play_list_sx + TEXT_BUTTON_PADDING, play_button_text_sy, WHITE, STR_PLAY_LIST);
        }

        int spead_sx = play_list_sx;
        if (trick_spead_mode != 1)
        {
            char spead_text[24];
            if (trick_spead_mode > 1)
                snprintf(spead_text, 24, "%dx%s", trick_spead_mode, STR_FAST_FORWARD);
            else
                snprintf(spead_text, 24, "%dx%s", -trick_spead_mode, STR_FAST_REWIND);
            int spead_dx = play_list_sx - BOTTOM_MESSAGE_MARIN_L;
            spead_sx = spead_dx - UiGetTextWidth(spead_text) - TEXT_BUTTON_PADDING * 2;
            UiDrawText(spead_sx + TEXT_BUTTON_PADDING, play_button_text_sy, WHITE, spead_text);
        }

        int progress_bar_sy = progress_bar_view_sy + PROGRESS_BAR_VIEW_PADDING_T;
        vita2d_draw_rectangle(progress_bar_view_sx, progress_bar_sy, PROGRESS_BAR_TOTAL_W, PROGRESS_BAR_TOTAL_H, PROGRESS_BAR_BG_COLOR);

        if (total_time > 0)
        {
            float percent = (double)current_time / (double)total_time;
            int progress_bar_w = PROGRESS_BAR_TOTAL_W * percent;
            vita2d_draw_rectangle(progress_bar_view_sx, progress_bar_sy, progress_bar_w, PROGRESS_BAR_TOTAL_H, PROGRESS_BAR_COLOR);

            int focus_view_sx = progress_bar_view_sx + progress_bar_w - PROGRESS_BAR_FOCUS_VIEW_W / 2;
            int focus_view_sy = progress_bar_sy - (PROGRESS_BAR_FOCUS_VIEW_H - PROGRESS_BAR_TOTAL_H) / 2;
            vita2d_draw_rectangle(focus_view_sx, focus_view_sy, PROGRESS_BAR_FOCUS_VIEW_W, PROGRESS_BAR_FOCUS_VIEW_H, PROGRESS_BAR_COLOR);
        }
    }
    else
    {
        resetTopStatusBarTitleScroll();
    }

    refreshListViewX();
    if (listview_current_sx < listview_dx)
        ListViewDraw(avplayListView);

    UiEndDrawing();
}

static void controlListview()
{
    TouchReaport *touch_reaport = &touch_pad.reaports[0];
    TouchStates *touch_states = &touch_reaport->states;
    TouchData *old_touch = &touch_reaport->old;

    showControlsView();

    if (touch_states->released_touched && !old_touch->long_hold)
    {
        int is_free_view_released = !isValidLocation(old_touch->x, old_touch->y, listview_sx, listview_sy, listview_dx, listview_dy);
        if (is_free_view_released)
        {
            avplayerCloseListView();
            return;
        }
    }

    ListViewEvent(avplayListView);

    int gamepad_mode = ListViewGetTouchMode(avplayListView);

    if (ctrl_pad.hold[PAD_UP] || ctrl_pad.hold2[PAD_LEFT_ANALOG_UP])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(avplayListView, 1);
        else
            ListViewMoveFocusPos(avplayListView, MOVE_TYPE_PREV);
    }
    else if (ctrl_pad.hold[PAD_DOWN] || ctrl_pad.hold2[PAD_LEFT_ANALOG_DOWN])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(avplayListView, 1);
        else
            ListViewMoveFocusPos(avplayListView, MOVE_TYPE_NEXT);
    }
    else if (ctrl_pad.hold[PAD_LEFT])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(avplayListView, 1);
        else
            ListViewMoveFocusPos(avplayListView, MOVE_TYPE_PREV_PAGE);
    }
    else if (ctrl_pad.hold[PAD_RIGHT])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(avplayListView, 1);
        else
            ListViewMoveFocusPos(avplayListView, MOVE_TYPE_NEXT_PAGE);
    }

    if (ctrl_pad.released[PAD_CANCEL] || ctrl_pad.released[PAD_TRIANGLE])
    {
        avplayerCloseListView();
    }
    else if (ctrl_pad.released[PAD_ENTER])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(avplayListView, 1);
        else
            onItemClick(avplayListView);
    }
    else if (ctrl_pad.released[PAD_START])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(avplayListView, 1);
        int top_pos = playlist_current_pos - ListViewGetDrawLines(avplayListView) / 2;
        ListViewRefreshPosByFocusPos(avplayListView, top_pos, playlist_current_pos, SCROLL_NONE);
    }
}

static int AvplayerControl()
{
    static int pressed_controls_view = 0;
    static int pressed_progress_bar = 0;
    static int disabale_close_controls_view = 0;
    static int auto_forward_mode = 0;
    static int forward_mode = 0;

    int enable_jump_to_time = 0;
    int reset_jump_time = 1;
    int reset_focus_time = 1;

    uint64_t cur_micros = 0, delta_micros = 0;

    readPad(&ctrl_pad);
    readTouch(&touch_pad);

    TouchReaport *touch_reaport = &touch_pad.reaports[0];
    TouchStates *touch_states = &touch_reaport->states;
    TouchData *old_touch = &touch_reaport->old;
    TouchData *current_touch = &touch_reaport->current;

    cur_micros = sceKernelGetProcessTimeWide();

    if (av_total_time > 0)
        av_current_time = sceAvPlayerCurrentTime(avplayer_handle);

    if (touch_states->pressed_touched)
    {
        if (!show_list_view && show_controls_view)
        {
            pressed_controls_view = isValidLocation(current_touch->x, current_touch->y, controls_view_sx, controls_view_sy, controls_view_dx, controls_view_dy);
            pressed_progress_bar = isValidLocation(current_touch->x, current_touch->y, progress_bar_view_sx, progress_bar_view_sy, progress_bar_view_dx, progress_bar_view_dy);
        }
    }

    if (!touch_states->current_touched && !touch_states->old_touched)
    {
        pressed_controls_view = 0;
        pressed_progress_bar = 0;
        disabale_close_controls_view = 0;
    }

    if (touch_pad.back_event_mode)
    {
        if (touch_pad.back_event_clicked)
            ctrl_pad.released[PAD_CANCEL] = 1;

        if (!show_list_view)
            goto CTRL_PAD;
    }

    if (show_list_view)
    {
        controlListview();
        goto END;
    }

    if (touch_states->current_touched)
    {
        if (pressed_progress_bar)
        {
            float percent = (float)(current_touch->x - progress_bar_view_sx) / (float)PROGRESS_BAR_TOTAL_W;
            av_focus_time = av_total_time * percent;
            reset_focus_time = 0;
        }

        if (!pressed_controls_view)
        {
            if (current_touch->beginning_long_motionless_hold)
            {
                auto_forward_mode = 1;
                if (trick_spead_mode != LONG_CLICK_TRICK_SPEED_MODE)
                {
                    trick_spead_mode = LONG_CLICK_TRICK_SPEED_MODE;
                    int trick_spead = trick_spead_mode * TRICK_SPEED_NORMAL;
                    sceAvPlayerSetTrickSpeed(avplayer_handle, trick_spead);
                }
            }

            if (touch_states->current_x_scrolled && !auto_forward_mode)
            {
                disabale_close_controls_view = 1;
                reset_jump_time = 0;

                int scroll_pixls = current_touch->x_scroll_cur_pixls;

                if (scroll_pixls < -4)
                {
                    forward_mode = 1;
                    int step_count = -scroll_pixls / 4; // 每滑动4像素，退后1秒
                    uint64_t step_time = (uint64_t)step_count * SMALL_JUMP_STEP_TIME;

                    if (av_jump_time > step_time)
                        av_jump_time -= step_time;
                    else if (av_jump_time > 0)
                        av_jump_time = 0;
                }
                else if (scroll_pixls > 4)
                {
                    forward_mode = 1;
                    int step_count = scroll_pixls / 4; // 每滑动4像素，前进1秒
                    uint64_t step_time = (uint64_t)step_count * SMALL_JUMP_STEP_TIME;

                    if (av_total_time > av_jump_time + step_time)
                        av_jump_time += step_time;
                    else if (av_total_time > 3000 && av_jump_time < av_total_time - 3000)
                        av_jump_time = av_total_time - 3000;
                }
            }
        }

        if (pressed_controls_view || auto_forward_mode || forward_mode)
        {
            showControlsView();
        }
    }
    else if (touch_states->released_touched)
    {
        if (pressed_progress_bar)
        {
            av_jump_time = av_focus_time;
            enable_jump_to_time = 1;
            reset_focus_time = 0;
            reset_jump_time = 0;
            pressed_progress_bar = 0;
        }

        if (auto_forward_mode)
        {
            if (trick_spead_mode != 1)
            {
                trick_spead_mode = 1;
                int trick_spead = trick_spead_mode * TRICK_SPEED_NORMAL;
                sceAvPlayerSetTrickSpeed(avplayer_handle, trick_spead);
            }
            auto_forward_mode = 0;
        }

        if (forward_mode)
        {
            forward_mode = 0;
        }

        if (!old_touch->long_hold)
        {
            if (show_controls_view)
            {
                int is_play_button_clicked = isValidLocation(old_touch->x, old_touch->y, play_button_sx, play_button_sy, play_button_dx, play_button_dy);
                int is_paly_mode_clicked = isValidLocation(old_touch->x, old_touch->y, paly_mode_sx, paly_mode_sy, paly_mode_dx, paly_mode_dy);
                int is_display_mode_clicked = isValidLocation(old_touch->x, old_touch->y, display_mode_sx, display_mode_sy, display_mode_dx, display_mode_dy);
                int is_play_list_clicked = 0;
                if (avplayListView)
                    is_play_list_clicked = isValidLocation(old_touch->x, old_touch->y, play_list_sx, play_list_sy, play_list_dx, play_list_dy);

                if (is_play_button_clicked)
                {
                    ctrl_pad.released[PAD_ENTER] = 1;
                }
                else if (is_play_list_clicked)
                {
                    ctrl_pad.released[PAD_TRIANGLE] = 1;
                }
                else if (is_paly_mode_clicked)
                {
                    ctrl_pad.released[PAD_START] = 1;
                }
                else if (is_display_mode_clicked)
                {
                    ctrl_pad.released[PAD_SELECT] = 1;
                }
            }

            if (!pressed_controls_view)
            {
                if (cur_micros - last_released_touch_micros < DOUBLE_CLICK_DELTA_MICROS)
                {
                    disabale_close_controls_view = 1;
                    ctrl_pad.released[PAD_ENTER] = 1;
                }
                else
                {
                    last_released_touch_micros = cur_micros;
                    ctrl_pad.released[PAD_SQUARE] = 1;
                }
            }
        }

        pressed_controls_view = 0;
    }

CTRL_PAD:

    if (ctrl_pad.current[PAD_L1] && ctrl_pad.current_hold_count[PAD_L1] >= 20)
    {                                     // 按住L1键20帧后启动热键
        if (ctrl_pad.released[PAD_ENTER]) // 从头播放
        {
            showControlsView();
            sceAvPlayerJumpToTime(avplayer_handle, 0);
        }

        goto END;
    }

    if (ctrl_pad.released[PAD_LEFT] && ctrl_pad.old_hold_count[PAD_LEFT] < 20)
    {
        showControlsView();
        reset_jump_time = 0;
        enable_jump_to_time = 1;

        if (av_current_time > BIG_JUMP_STEP_TIME)
            av_jump_time = av_current_time - BIG_JUMP_STEP_TIME;
        else if (av_current_time > 0)
            av_jump_time = 0;
    }
    else if (ctrl_pad.released[PAD_RIGHT] && ctrl_pad.old_hold_count[PAD_RIGHT] < 20)
    {
        showControlsView();
        reset_jump_time = 0;
        enable_jump_to_time = 1;

        if (av_total_time > av_current_time + BIG_JUMP_STEP_TIME)
            av_jump_time = av_current_time + BIG_JUMP_STEP_TIME;
        else if (av_total_time > 3000 && av_current_time < av_total_time - 3000) // 留3秒
            av_jump_time = av_total_time - 3000;
    }
    else if (ctrl_pad.current_hold_count[PAD_LEFT] >= 20)
    {
        showControlsView();
        reset_jump_time = 0;

        if (ctrl_pad.current_hold_count[PAD_LEFT] % 15 == 0) // 每隔15帧响应一次
        {
            int multiple = ctrl_pad.current_hold_count[PAD_LEFT] / 15; // 根据按下次数增大步时
            if (multiple > 60)                                         // 限制最大步时
                multiple = 60;
            uint64_t step = SMALL_JUMP_STEP_TIME * multiple;

            if (av_jump_time > step)
                av_jump_time -= step;
            else if (av_jump_time > 0)
                av_jump_time = 0;
        }
    }
    else if (ctrl_pad.current_hold_count[PAD_RIGHT] >= 20)
    {
        showControlsView();
        reset_jump_time = 0;

        if (ctrl_pad.current_hold_count[PAD_RIGHT] % 15 == 0) // 每隔15帧响应一次
        {
            int multiple = ctrl_pad.current_hold_count[PAD_RIGHT] / 15; // 根据按下次数增大步时
            if (multiple > 60)                                          // 限制最大步时
                multiple = 60;
            uint64_t step = SMALL_JUMP_STEP_TIME * multiple;

            if (av_total_time > step && av_jump_time < av_total_time - step)
                av_jump_time += step;
            else if (av_total_time > 3000 && av_jump_time < av_total_time - 3000)
                av_jump_time = av_total_time - 3000;
        }
    }
    else if (ctrl_pad.released[PAD_UP])
    {
        showControlsView();

        int tmp_trick_spead_mode = 2;
        if (trick_spead_mode > 1)
            tmp_trick_spead_mode = trick_spead_mode * 2;
        else if (trick_spead_mode < 1)
            tmp_trick_spead_mode = trick_spead_mode / 2;
        if (tmp_trick_spead_mode > MAX_TRICK_SPEED_MODE_NUM)
            tmp_trick_spead_mode = MAX_TRICK_SPEED_MODE_NUM;
        else if (tmp_trick_spead_mode == -1 || tmp_trick_spead_mode == 0)
            tmp_trick_spead_mode = 1;
        if (tmp_trick_spead_mode != trick_spead_mode)
        {
            trick_spead_mode = tmp_trick_spead_mode;
            int trick_spead = trick_spead_mode * TRICK_SPEED_NORMAL;
            sceAvPlayerSetTrickSpeed(avplayer_handle, trick_spead);
        }
    }
    else if (ctrl_pad.released[PAD_DOWN])
    {
        showControlsView();

        int tmp_trick_spead_mode = -2;
        if (trick_spead_mode > 1)
            tmp_trick_spead_mode = trick_spead_mode / 2;
        else if (trick_spead_mode < 1)
            tmp_trick_spead_mode = trick_spead_mode * 2;
        if (tmp_trick_spead_mode < -MAX_TRICK_SPEED_MODE_NUM)
            tmp_trick_spead_mode = -MAX_TRICK_SPEED_MODE_NUM;
        else if (tmp_trick_spead_mode == -1 || tmp_trick_spead_mode == 0)
            tmp_trick_spead_mode = 1;
        if (tmp_trick_spead_mode != trick_spead_mode)
        {
            trick_spead_mode = tmp_trick_spead_mode;
            int trick_spead = trick_spead_mode * TRICK_SPEED_NORMAL;
            sceAvPlayerSetTrickSpeed(avplayer_handle, trick_spead);
        }
    }
    else if (ctrl_pad.released[PAD_CANCEL])
    {
        if (trick_spead_mode != 1)
        {
            showControlsView();
            trick_spead_mode = 1;
            int trick_spead = trick_spead_mode * TRICK_SPEED_NORMAL;
            sceAvPlayerSetTrickSpeed(avplayer_handle, trick_spead);
        }
        else
        {
            AvplayerClose();
            return 0;
        }
    }
    else if (ctrl_pad.released[PAD_ENTER])
    {
        showControlsView();
        if (avplayer_playing)
            AvplayerPause();
        else
            AvplayerResume();
    }
    else if (ctrl_pad.released[PAD_SQUARE])
    {
        if (show_controls_view)
        {
            if (!disabale_close_controls_view)
                closeControlsView();
        }
        else
        {
            showControlsView();
        }
    }
    else if (ctrl_pad.released[PAD_TRIANGLE])
    {
        if (avplayListView)
        {
            avplayerOpenListView();
            ListViewSetGamepadMode(avplayListView, 1);
        }
    }
    else if (ctrl_pad.released[PAD_L1] && ctrl_pad.old_hold_count[PAD_L1] < 20)
    {
        int n = playlist_current_pos - 1;
        if (n >= 0)
            AvplayerOpenFromListByNumber(n, avplayer_paly_mode, 1);
    }
    else if (ctrl_pad.released[PAD_R1] && ctrl_pad.old_hold_count[PAD_R1] < 20)
    {
        int n = playlist_current_pos + 1;
        if (n < avplaypList->length)
            AvplayerOpenFromListByNumber(n, avplayer_paly_mode, 1);
    }
    else if (ctrl_pad.released[PAD_SELECT])
    {
        showControlsView();
        if (avplayer_config.display_mode == DISPLAY_PRESERVE_FILL)
            avplayer_config.display_mode = DISPLAY_FULL_FILL;
        else
            avplayer_config.display_mode = DISPLAY_PRESERVE_FILL;
        saveAvplayerConfig();
    }
    else if (ctrl_pad.released[PAD_START])
    {
        showControlsView();
        avplayer_paly_mode++;
        if (avplayer_paly_mode > N_PLAY_MODE_OPTIOMS - 1)
            avplayer_paly_mode = 0;
        if (avplayer_paly_mode == PLAY_MODE_LOOP_SINGLE)
            sceAvPlayerSetLooping(avplayer_handle, 1);
        else
            sceAvPlayerSetLooping(avplayer_handle, 0);
    }

END:

    cur_micros = sceKernelGetProcessTimeWide();

    if (change_controls_view_type != CHANGE_CONTROLS_VIEW_NONE)
    {
        delta_micros = cur_micros - change_controls_view_begin_micros;

        if (delta_micros > CHANGE_CONTROLS_VIEW_DELTA_MICROS)
        {
            if (change_controls_view_type == CHANGE_CONTROLS_VIEW_SHOW)
            {
                show_controls_view_final_micros = cur_micros + CONTROLS_VIEW_SHOW_MICROS;
                show_controls_view = 1;
                change_controls_view_type = CHANGE_CONTROLS_VIEW_NONE;
            }
            else if (change_controls_view_type == CHANGE_CONTROLS_VIEW_CLOSE)
            {
                show_controls_view_final_micros = 0;
                show_controls_view = 0;
                change_controls_view_type = CHANGE_CONTROLS_VIEW_NONE;
            }
        }
    }

    if (show_controls_view_final_micros <= cur_micros)
        show_controls_view = 0;

    if (reset_jump_time)
        av_jump_time = av_current_time;
    if (reset_focus_time)
        av_focus_time = av_current_time;

    delta_micros = cur_micros - jump_last_micros;
    if (delta_micros > jump_delay_micros)
    {
        if (av_jump_time != av_current_time)
            enable_jump_to_time = 1;
        jump_last_micros = cur_micros;
    }

    if (enable_jump_to_time)
    {
        sceAvPlayerJumpToTime(avplayer_handle, av_jump_time);
        av_focus_time = av_jump_time;
    }

    return 0;
}

void AvplayerRun()
{
    if (!avplayer_ready)
        return;

    uint64_t cur_micros = 0, delta_micros = 0;
    uint64_t draw_last_micros = 0;
    uint64_t suspend_delta_micros = (30.f * 1000000.f);

    cur_micros = sceKernelGetProcessTimeWide();
    save_last_micros = cur_micros;
    save_delay_micros = SAVE_DELAY_MICROS;
    jump_last_micros = cur_micros;
    jump_delay_micros = JUMP_DELAY_MICROS;

    showControlsView();

    resetPad(&ctrl_pad);
    resetTouch(&touch_pad);
    resetTopStatusBarTitleScroll();
    UiStartDrawing();
    UiEndDrawing();

    while (!avplayer_close)
    {
        if (!sceAvPlayerIsActive(avplayer_handle))
        {
            int stop = 1;
            if (avplaypList && (avplayer_paly_mode == PLAY_MODE_SEQUENTIAL || avplayer_paly_mode == PLAY_MODE_LOOP_ALL))
            {
                stop = 0;
                int n = playlist_current_pos + 1;
                if (n >= avplaypList->length)
                {
                    if (avplayer_paly_mode == PLAY_MODE_LOOP_ALL)
                        n = 0;
                    else
                        stop = 1;
                }
                if (!stop)
                    AvplayerOpenFromListByNumber(n, avplayer_paly_mode, 1);
            }

            if (stop)
            {
                AvplayerClose();
                break;
            }
        }

        vita2d_texture *texture = AvPlayerGetVideoTexture();
        if (texture)
        {
            AvplayerDraw(texture);
            avplayerLockSuspend();
            draw_last_micros = sceKernelGetProcessTimeWide();
        }
        else
        {
            AvplayerDraw(old_tex);
            cur_micros = sceKernelGetProcessTimeWide();
            if (cur_micros - draw_last_micros > suspend_delta_micros)
                avplayerUnlockSuspend();
        }

        AvplayerControl();
    }

    resetPad(&ctrl_pad);
    resetTouch(&touch_pad);
    resetTopStatusBarTitleScroll();
    avplayerUnlockSuspend();
}
